use actix_web::{
    get, post, web, App, web::Form,
    HttpResponse, HttpServer
};
use serde::Deserialize;
use passwords::PasswordGenerator;
use html_escape;

#[derive(Debug, Deserialize)]
struct ReqGenerator {
        length: Option<String>,
        numbers: Option<bool>,
        lowercase_letters: Option<bool>,
        uppercase_letters: Option<bool>,
        symbols: Option<bool>,
        spaces: Option<bool>,
        exclude_similar_characters: Option<bool>,
}

struct Generator {
        length: usize,
        numbers: bool,
        lowercase_letters: bool,
        uppercase_letters: bool,
        symbols: bool,
        spaces: bool,
        exclude_similar_characters: bool,
}


async fn failed_request() -> HttpResponse {
    HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(include_str!("../static/failed_request.html"))
}

#[get("/")]
async fn index() -> HttpResponse {
    HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(include_str!("../static/generate_passwords.html"))
}

#[post("/generate")]
async fn generate_password(form_gen: Form<ReqGenerator>) -> HttpResponse {
    if form_gen.length.is_none() {
        return HttpResponse::Ok()
            .content_type("text/html; charset=utf-8")
            .body(include_str!("../static/failed_request.html"));
    }

    let mut gen: Generator;

    match form_gen.length.clone().unwrap().parse::<usize>() {
        Ok(l) => {
            gen = Generator {
                length: l,
                numbers: false,
                lowercase_letters: false,
                uppercase_letters: false,
                symbols: false,
                spaces: false,
                exclude_similar_characters: false,
            };
        },
        Err(_) => {
            return HttpResponse::Ok()
                .content_type("text/html; charset=utf-8")
                .body(include_str!("../static/failed_request.html"));
        },
    }

    let mut all_unchecked = true;
    
    if form_gen.numbers.is_some() {
        gen.numbers = true;
        all_unchecked = false;
    }

    if form_gen.lowercase_letters.is_some() {
        gen.lowercase_letters = true;
        all_unchecked = false;
    }

    if form_gen.uppercase_letters.is_some() {
        gen.uppercase_letters = true;
        all_unchecked = false;
    }

    if form_gen.symbols.is_some() {
        gen.symbols = true;
        all_unchecked = false;
    }

    if form_gen.spaces.is_some() {
        gen.spaces = true;
        all_unchecked = false;
    }

    if form_gen.exclude_similar_characters.is_some() {
        gen.exclude_similar_characters = true;
        all_unchecked = false;
    }

    if all_unchecked {
        return HttpResponse::Ok()            
            .content_type("text/html; charset=utf-8")
            .body(include_str!("../static/failed_request.html"));
    }
    
    let pw = PasswordGenerator {
        length: gen.length,
        numbers: gen.numbers,
        lowercase_letters: gen.lowercase_letters,
        uppercase_letters: gen.uppercase_letters,
        symbols: gen.symbols,
        spaces: gen.spaces,
        exclude_similar_characters: gen.exclude_similar_characters,
        strict: true,
    };

    let mut pw_string = String::from("");

    match pw.generate(5) {
        Ok(s) => {
            for p in s {
                pw_string = format!("{}<tr><td style=\"border: 1px solid grey; border-radius: 6px; padding: 6px;\">{}</td><tr>", pw_string,
                                    html_escape::encode_text(&p));
            }
        }
        Err(_) => {
            pw_string = format!("Failed to generate passwords!");
        }
    }
    
    HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(format!("<h1>Generated passwords:</h1><table>{}</table></br></br><a href=\"/\">Back</a>", pw_string))
}

#[actix_web::main]
async fn start_web() -> std::io::Result<()> {
    HttpServer::new(move || {
        App::new()
            .service(index)
            .service(generate_password)
            .default_service(web::to(failed_request))
    })
        .bind(("0.0.0.0", 8080))?
        .workers(1)
        .run()
        .await
}


fn main() -> std::io::Result<()> {
    start_web()
}
